package it.trekker.sensors;

import java.util.ArrayList;
import java.util.List;

import android.app.Service;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class SensorsListenerService extends Service implements SensorEventListener {
	
	private static final String TAG = SensorsListenerService.class.getSimpleName();
	
	private List<IAccelerometerListener> accelerometerListeners = null;
	private List<IOrientationListener> orientationListeners = null;
	private final IBinder mBinder = new SensorsBinder();
	private SensorManager sensorManager = null;
	private Sensor accelerometerSensor = null;
	private Sensor orientationSensor = null;
	
	
	/* Binder class for client */
	public class SensorsBinder extends Binder {
		public SensorsListenerService getService() {
			return SensorsListenerService.this;
		}
	} 

	@Override
	public IBinder onBind(Intent intent) {
		
		Log.d(TAG, "[SERVICE] OnBind");
		
		return mBinder;
	}
	
	@Override
	public void onCreate() {

		Log.d(TAG, "[SERVICE] OnCreate");
		
		super.onCreate();
		
		accelerometerListeners = new ArrayList<IAccelerometerListener>();
		orientationListeners = new ArrayList<IOrientationListener>();
		
		sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
		accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		sensorManager.registerListener(this, accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);
		
		orientationSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
		sensorManager.registerListener(this, orientationSensor, SensorManager.SENSOR_DELAY_UI);
		
	}
	
	@Override
	public void onDestroy() {
		super.onDestroy();
		
		Log.d(TAG, "[SERVICE] OnDestroy");
		
		sensorManager.unregisterListener(this);
	}
	
	

	
	//***************************************************************//
	//************************ Utils methods ************************//
	//***************************************************************//
	
	public void addAccelerometerListener(IAccelerometerListener listener) {
		accelerometerListeners.add(listener);
	}
	
	public void removeAccelerometerListener(IAccelerometerListener listener) {
		accelerometerListeners.remove(listener);
	}
	
	public void addOrientationListener(IOrientationListener listener) {
		orientationListeners.add(listener);
	}
	
	public void removeOrientationListener(IOrientationListener listener) {
		orientationListeners.remove(listener);
	}
	
	float[] gravity = new float[3];
	public float[] simpleAccelerationFilter(float[] acceleration) {
		final float alpha = (float) 0.8;
		float[] result = new float[3];
		
		gravity[0] = alpha * gravity[0] + (1 - alpha) * acceleration[0];
		gravity[1] = alpha * gravity[1] + (1 - alpha) * acceleration[1];
		gravity[2] = alpha * gravity[2] + (1 - alpha) * acceleration[2];
		
		result[0] = acceleration[0] - gravity[0];
		result[1] = acceleration[1] - gravity[1];
		result[2] = acceleration[2] - gravity[2];
		
		return result;
	}
	
	//last result storage - keep definition outside of this function, eg. in wrapping object
	float[] accel = new float[3];
	public float[] rampingAccelerationFilter(float[] acceleration) {
		//ramp-speed - play with this value until satisfied
		final float kFilteringFactor = 0.1f;
		
		float[] result = new float[3];

		//acceleration[] is the input from the sensor

		//high-pass filter to eleminate gravity
		accel[0] = acceleration[0] * kFilteringFactor + accel[0] * (1.0f - kFilteringFactor);
		accel[1] = acceleration[1] * kFilteringFactor + accel[1] * (1.0f - kFilteringFactor);
		accel[2] = acceleration[2] * kFilteringFactor + accel[2] * (1.0f - kFilteringFactor);
		
		//result[] is the filtered result
		result[0] = acceleration[0] - accel[0];
		result[1] = acceleration[1] - accel[1];
		result[2] = acceleration[2] - accel[2];
		
		return result;
	}
	
	public float calculateAcceleration(float[] coord) {
		
		float sum = coord[0]*coord[0] + coord[1]*coord[1] + coord[2]*coord[2];
		float acceleration = (float) Math.sqrt(sum);
		
//		return (acceleration - SensorManager.GRAVITY_EARTH);
		return acceleration;
	}
	
	
	
	
	
	//***************************************************************//
	//***************** SensorEventListener methods *****************//
	//***************************************************************//
	
	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		
		synchronized (this) {
			switch(event.sensor.getType()) {
				case Sensor.TYPE_ACCELEROMETER:
					float[] result = simpleAccelerationFilter(event.values);
					AccelerometerCoord coord = new AccelerometerCoord(result);
					float acceleration = calculateAcceleration(event.values);
					for(IAccelerometerListener listener : accelerometerListeners) {
						listener.updateAccelCoord(coord);
						listener.updateAcceleration(acceleration);
					}
					break;
					
				case Sensor.TYPE_ORIENTATION:
					OrientationCoord orientationCoord = new OrientationCoord(event.values);
					for(IOrientationListener listener : orientationListeners) {
						listener.updateOrientation(orientationCoord);
					}
					break;
			}
		}
	}

}
